using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;


namespace TSharp.Core.Web
{
    /// <summary>
    /// FROM:http://www.codeproject.com/KB/aspnet/CaptchaImage.aspx
    /// Summary description for CaptchaImage.
    /// </summary>
    public class CaptchaImage : IDisposable
    {
        // Public properties (all read-only).
        /// <summary>
        /// Gets the text.
        /// </summary>
        /// <value>The text.</value>
        public string Text
        {
            get { return this.text; }
        }
        /// <summary>
        /// Gets the image.
        /// </summary>
        /// <value>The image.</value>
        public Bitmap Image
        {
            get
            {
                return this.GenerateImage();
            }
        }
        /// <summary>
        /// Gets the width.
        /// </summary>
        /// <value>The width.</value>
        public int Width
        {
            get { return this.width; }
        }
        /// <summary>
        /// Gets the height.
        /// </summary>
        /// <value>The height.</value>
        public int Height
        {
            get { return this.height; }
        }

        // Internal properties.
        private string text;
        private int width;
        private int height;
        private string familyName;
        //    private Bitmap image;

        // For generating random numbers.
        private Random random = new Random();

        /// <summary>
        /// Initializes a new instance of the <see cref="CaptchaImage"/> class.
        /// </summary>
        /// <param name="s">The s.</param>
        public CaptchaImage(string s)
            : this(s, 200, 30)
        {
        }
        // ====================================================================
        // Initializes a new instance of the CaptchaImage class using the
        // specified text, width and height.
        // ====================================================================
        /// <summary>
        /// Initializes a new instance of the <see cref="CaptchaImage"/> class.
        /// </summary>
        /// <param name="s">The s.</param>
        /// <param name="width">The width.</param>
        /// <param name="height">The height.</param>
        public CaptchaImage(string s, int width, int height)
            : this(s, width, height, "Consolas")
        {
        }

        // ====================================================================
        // Initializes a new instance of the CaptchaImage class using the
        // specified text, width, height and font family.
        // ====================================================================
        /// <summary>
        /// Initializes a new instance of the <see cref="CaptchaImage"/> class.
        /// </summary>
        /// <param name="s">The s.</param>
        /// <param name="width">The width.</param>
        /// <param name="height">The height.</param>
        /// <param name="familyName">Name of the family.</param>
        public CaptchaImage(string s, int width, int height, string familyName)
        {
            this.text = s;
            this.SetDimensions(width, height);
            this.SetFamilyName(familyName);

        }

        // ====================================================================
        // This member overrides Object.Finalize.
        // ====================================================================
        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="CaptchaImage"/> is reclaimed by garbage collection.
        /// </summary>
        ~CaptchaImage()
        {
            Dispose(false);
        }

        // ====================================================================
        // Releases all resources used by this object.
        // ====================================================================
        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        public void Dispose()
        {
            GC.SuppressFinalize(this);
            this.Dispose(true);
        }

        // ====================================================================
        // Custom Dispose method to clean up unmanaged resources.
        // ====================================================================
        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            //if (disposing)
            // Dispose of the bitmap.
            // this.image.Dispose();
        }

        // ====================================================================
        // Sets the image width and height.
        // ====================================================================
        private void SetDimensions(int width, int height)
        {
            // Check the width and height.
            if (width <= 0)
                throw new ArgumentOutOfRangeException("width", width, "Argument out of range, must be greater than zero.");
            if (height <= 0)
                throw new ArgumentOutOfRangeException("height", height, "Argument out of range, must be greater than zero.");
            this.width = width;
            this.height = height;
        }

        // ====================================================================
        // Sets the font used for the image text.
        // ====================================================================
        private void SetFamilyName(string familyName)
        {
            // If the named font is not installed, default to a system font.
            try
            {
                Font font = new Font(this.familyName, 13F);
                this.familyName = familyName;
                font.Dispose();
            }
            catch (Exception)
            {
                this.familyName = System.Drawing.FontFamily.GenericSerif.Name;
            }
        }

        // ====================================================================
        // Creates the bitmap image.
        // ====================================================================
        private Bitmap GenerateImage()
        {
            // Create a new 32-bit bitmap image.
            Bitmap bitmap = new Bitmap(this.width, this.height, PixelFormat.Format32bppArgb);

            // Create a graphics object for drawing.
            using (Graphics g = Graphics.FromImage(bitmap))
            {
                g.SmoothingMode = SmoothingMode.AntiAlias;
                Rectangle rect = new Rectangle(0, 0, this.width, this.height);

                // Fill in the background.
                using (HatchBrush hatchBrush = new HatchBrush(HatchStyle.SmallConfetti, Color.LightGray, Color.White))
                    g.FillRectangle(hatchBrush, rect);

                // Draw the text.
                using (HatchBrush hatchBrush = new HatchBrush(HatchStyle.LargeConfetti, Color.LightGray, Color.DarkGray))
                {
                    // Set up the text format.
                    StringFormat format = new StringFormat();
                    format.Alignment = StringAlignment.Center;
                    format.LineAlignment = StringAlignment.Center;

                    // Create a path using the text and warp it randomly.
                    GraphicsPath path = new GraphicsPath();

                    {
                        // Set up the text font.
                        Font font;
                        SizeF size;
                        float fontSize = rect.Height + 1;
                        // Adjust the font size until the text fits within the image.
                        do
                        {
                            fontSize--;
                            var fontTmp = new Font(this.familyName, fontSize, FontStyle.Bold);
                            size = g.MeasureString(this.text, fontTmp);
                            if (size.Width > rect.Width)
                            {
                                font = fontTmp;
                                break;
                            }
                            else
                                fontTmp.Dispose();
                        } while (true);

                        path.AddString(this.text, font.FontFamily, (int)font.Style, font.Size, rect, format);
                        font.Dispose();
                    }
                    using (Matrix matrix = new Matrix())
                    {
                        matrix.Translate(0F, 0F);

                        float v = 4F;
                        PointF[] points =
			                {
				                new PointF(this.random.Next(rect.Width) / v, this.random.Next(rect.Height) / v),
				                new PointF(rect.Width - this.random.Next(rect.Width) / v, this.random.Next(rect.Height) / v),
				                new PointF(this.random.Next(rect.Width) / v, rect.Height - this.random.Next(rect.Height) / v),
				                new PointF(rect.Width - this.random.Next(rect.Width) / v, rect.Height - this.random.Next(rect.Height) / v)
			                };
                        path.Warp(points, rect, matrix, WarpMode.Perspective, 0F);
                        points = null;
                    }

                    g.FillPath(hatchBrush, path);

                    // Add some random noise.
                    int m = Math.Max(rect.Width, rect.Height);
                    for (int i = 0; i < (int)(rect.Width * rect.Height / 30F); i++)
                    {
                        int x = this.random.Next(rect.Width);
                        int y = this.random.Next(rect.Height);
                        int w = this.random.Next(m / 50);
                        int h = this.random.Next(m / 50);
                        g.FillEllipse(hatchBrush, x, y, w, h);
                    }
                }
                g.Dispose();
            }
            // Set the image.
            return bitmap;
        }
    }
}
